package com.kstudio.videocapture.listimage;

import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;

import com.kstudio.videocapture.gl.ImageGL2D;
import com.kstudio.videocapture.gl.LinearListGL2D;
import com.kstudio.videocapture.util.KLog;
import com.kstudio.videocapture.util.KUtil;

public class KImageListViewSubListGL extends GLSurfaceView implements Renderer {
    private final KLog log = new KLog("KImageList");

    private static final int STATE_INIT_NOT  = 0x0000;
    private static final int STATE_INIT_DO   = 0x0001;
    private static final int STATE_INIT_DONE = 0x0002;

    private static final int INVALIDATE_NONE        = 0x0000;
    private static final int INVALIDATE_DO          = 0x0001;
    private static final int INVALIDATE_DOING       = 0x0002;
    private static final int INVALIDATE_INIT        = 0x0004;
    private static final int INVALIDATE_INIT_DONE   = 0x0008;

    private static final int ACTION_MODE_NONE = 0x0000;
    private static final int ACTION_MODE_DRAG = 0x0001;
    private static final int ACTION_MODE_LIST_BOX_SCROLL = 0x0004;

    private ImageGL2D   mMainImage;
    private Bitmap      mMainBitmap;

    private LinearListGL2D mLinearList;

    private int mWidth;
    private int mHeight;

    private float mScale;
    private float mMinScale;
    private float mMaxScale;

    private int mState;
    private int mInvalidate;
    private int mInvalidateCnt;

    private int     mActionMode             = ACTION_MODE_NONE;
    private PointF  mActionTransStartPos    = new PointF();
    private PointF  mActionZoomMidPos       = new PointF();
    private float   mActionZoomStartDist;
    private float   mActionZoomSavedPosX;
    private float   mActionZoomSavedPosY;
    private float   mActionZoomSavedScale;

    private int   mActionLineSavedLeft;
    private int   mActionLineSavedRight;
    private int   mActionLineSavedBottom;
    private int   mActionLineSavedTop;
    private int   mActionLineMode;

//    private KLoadBitmapAsync mLinearListAsyncTask;
    private ArrayList<KImageListItem> mImageList;
    private int mLinearListBmWidth;
    private int mLinearListBmHeight;

    private int mLinearListPaddingImage;
    private int mLinearListPaddingLeft;
    private int mLinearListPaddingRight;
    private int mLinearListPaddingBottom;
    private boolean mLinearListExist;

    private int mLinearList_Index;
    private int mLinearList_InStartPos;
    private int mLinearList_InEndPos;
    private int mLinearList_LoadedStartPos;
    private int mLinearList_LoadedEndPos;

    private int mActionListBoxMode;

    public KImageListViewSubListGL(Context context) {
        this(context, null);
    }

    public KImageListViewSubListGL(Context context, AttributeSet attrs) {
        super(context, attrs);
        log.i("KImageEditViewWithGL");

        mInvalidate = INVALIDATE_NONE;
        mImageList = null;
        mLinearListExist = false;
//        mLinearListAsyncTask = null;
//        mBitmapDoubleCache = new BitmapDoubleCache(context, this);

        this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        this.setRenderer(this);
        this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        this.setZOrderMediaOverlay(true);

        setAttribute(context, attrs, 0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        log.i("KImageEditViewWithGL : onSurfaceCreated");

        gl.glDisable(GL10.GL_DITHER);

        gl.glClearColor(0,0,0,0);

        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glClearDepthf(1.0f);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glDepthFunc(GL10.GL_LEQUAL);

        gl.glEnable(GL10.GL_BLEND);
//      gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE);
        gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
        gl.glEnable(GL10.GL_ALPHA_TEST);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        log.i("KImageEditViewWithGL : onSurfaceChanged");

        mWidth = width;
        mHeight = height;

        gl.glViewport(0, 0, width,height);

        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();

//      gl.glOrthof(0f, 1f, 0f, 1f, -1f, 1f);
        gl.glOrthof(0, width, 0, height, -1, 1);

    }

    @Override
    public void onDrawFrame(GL10 gl) {
        if((mInvalidate & INVALIDATE_INIT) == INVALIDATE_INIT) {
            init(gl);
            mInvalidate ^= INVALIDATE_INIT;
            mInvalidate |= INVALIDATE_DO | INVALIDATE_INIT_DONE;
        }

        if((mInvalidate & (INVALIDATE_INIT_DONE | INVALIDATE_DO)) == (INVALIDATE_INIT_DONE | INVALIDATE_DO) ||
           (mInvalidate & (INVALIDATE_INIT_DONE | INVALIDATE_DOING)) == (INVALIDATE_INIT_DONE | INVALIDATE_DOING)) {
            draw(gl);

            if((mInvalidate & INVALIDATE_DO) == INVALIDATE_DO) {
                mInvalidate ^= INVALIDATE_DO;
                mInvalidate |= INVALIDATE_DOING;
                mInvalidateCnt = 1;
            } else if((mInvalidate & INVALIDATE_DOING) == INVALIDATE_DOING) {
                if(mInvalidateCnt < 5)
                    mInvalidateCnt++;
                else
                    mInvalidate ^= INVALIDATE_DOING;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;

        if(action == MotionEvent.ACTION_DOWN) {
            mActionTransStartPos.set(event.getX(), event.getY());

            if(mLinearList.checkInsideFromView(event.getX(), event.getY())) {
                mLinearList.startScrollX(event.getX());
                mActionMode = ACTION_MODE_LIST_BOX_SCROLL;
            } else {
                savedActionData();
                mActionMode = ACTION_MODE_DRAG;
            }
            return true;
        } else {
            switch(mActionMode) {
                case ACTION_MODE_DRAG :
                    return action_Drag(event);
                case ACTION_MODE_LIST_BOX_SCROLL :
                    return action_ListBox_Scroll(event);
            }
        }
        return super.onTouchEvent(event);
    }

    public static int computeLinearListStartPos(int index, int totalSize, int listSize) {
        int computeStartPos = 0;

        if(totalSize < listSize || index < listSize / 2) {
            computeStartPos = 0;
        } else if(index > totalSize - listSize / 2) {
            computeStartPos = totalSize - listSize;
        } else {
            computeStartPos = index - listSize / 2;
        }

        return computeStartPos;
    }

    public void setInit(ArrayList<KImageListItem> savedData, int index) {
        mMainBitmap = KUtil.loadBitmap(savedData.get(index).getPath());

        if(savedData.size() > 1) {
            mImageList = savedData;
            mLinearListExist = true;
            mLinearList_Index = index;
        }

        mInvalidate = INVALIDATE_INIT;
    }

    private void setAttribute(Context c, AttributeSet attrs, int defStyle) {
        int[] ids = new int[attrs.getAttributeCount()];
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            ids[i] = attrs.getAttributeNameResource(i);
        }

        TypedArray a = c.obtainStyledAttributes(attrs, ids, defStyle, 0);

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            String attrName = attrs.getAttributeName(i);
            if (attrName == null)
                continue;

            if (attrName.equals("image_linearlist_padding_left")) {
                mLinearListPaddingLeft = a.getDimensionPixelSize(i, 0);
            } else if (attrName.equals("image_linearlist_padding_right")) {
                mLinearListPaddingRight = a.getDimensionPixelSize(i, 0);
            } else if (attrName.equals("image_linearlist_padding_bottom")) {
                mLinearListPaddingBottom = a.getDimensionPixelSize(i, 0);
            } else if (attrName.equals("image_linearlist_padding_iamge")) {
                mLinearListPaddingImage = a.getDimensionPixelSize(i, 0);
            } else if (attrName.equals("image_linearlist_base_size_ref")) {
                Bitmap bm = BitmapFactory.decodeResource(a.getResources(), a.getResourceId(i, 0));
                mLinearListBmWidth = bm.getWidth();
                mLinearListBmHeight = bm.getHeight();
                bm.recycle();
            }
        }

        a.recycle();

    }

    private void init(GL10 gl) {
        log.i("KImageEditViewWithGL : init");

        int l = 0;
        int t = 0;
        int r = 0;
        int b = 0;

        if(mMainImage == null) {
            mMainImage = ImageGL2D.createFromBmp(gl, mMainBitmap);
            int imageWidth = mMainImage.getWidth();
            int imageHeight = mMainImage.getHeight();

            l = 0;
            r = l + imageWidth;
            b = 0;
            t = b + imageHeight;
            mMainImage.setLayout(l, r, b, t);

            /* Linear List */
            int listCnt = mWidth / mLinearListBmWidth;
            int linearlistWidth = listCnt * mLinearListBmWidth + (listCnt -1) * mLinearListPaddingImage;

            while((mWidth - linearlistWidth) < (mLinearListPaddingLeft + mLinearListPaddingRight)) {
                listCnt--;
                linearlistWidth = listCnt * mLinearListBmWidth + (listCnt -1) * mLinearListPaddingImage;
            }

            int linearlistLeft = (mWidth - linearlistWidth) / 2;

            l = linearlistLeft;
            r = l + linearlistWidth;
            b = mLinearListPaddingBottom;
            t = b + mLinearListBmHeight;

            mLinearList = LinearListGL2D.create(gl, null, mHeight); /* KKH */
            mLinearList.setAttribute(mLinearListBmWidth, mLinearListBmHeight, mLinearListPaddingImage, listCnt);
            mLinearList.setLayout(l, r, b, t);

            if(mImageList.size() < listCnt || mLinearList_Index < listCnt / 2) {
                mLinearList_InStartPos = 0;
                mLinearList_InEndPos = listCnt;
            } else if(mLinearList_Index > mImageList.size() - listCnt / 2){
                mLinearList_InStartPos = mImageList.size() - listCnt;
                mLinearList_InEndPos = mImageList.size();
            } else {
                mLinearList_InStartPos = mLinearList_Index - listCnt / 2;
                mLinearList_InEndPos = mLinearList_InStartPos + listCnt;
            }

            mMinScale = 1f;

            if(imageWidth > mWidth ||
              imageHeight > mHeight ||
              (imageWidth < mWidth && imageHeight < mHeight)) {
                float scaleWidth = (float)((double) mWidth / (double) imageWidth);
                float scaleHeight = (float)((double) mHeight / (double) imageHeight);

                if(scaleWidth > scaleHeight) {
                    mMinScale = scaleHeight;
                } else {
                    mMinScale = scaleWidth;
                }
            }

            if(mMinScale < 1)  mMaxScale = 4f;
            else               mMaxScale = mMinScale * 4f;

            mScale = mMinScale;
            mMainImage.scale(mMinScale, mMinScale);
            move(0f, 0f);
        }
    }

    private synchronized void draw(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        mMainImage.draw(gl);

        mLinearList.draw(gl);

        gl.glEnable(GL10.GL_BLEND);
//      gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE);
        gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
        gl.glEnable(GL10.GL_ALPHA_TEST);
    }

    private boolean action_Drag(MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;

        switch(action) {
          case MotionEvent.ACTION_MOVE:
              float moveX = mActionZoomSavedPosX + (event.getX() - mActionTransStartPos.x) / mMainImage.getScaleX();
              float moveY = mActionZoomSavedPosY + (mActionTransStartPos.y - event.getY()) / mMainImage.getScaleY();
              move(moveX , moveY);
              return true;
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_POINTER_UP:
              mActionMode = ACTION_MODE_NONE;
              return true;
        }
        return false;
    }

    private synchronized boolean action_ListBox_Scroll(MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;

        switch(action) {
          case MotionEvent.ACTION_MOVE:
              mLinearList.scrollX(event.getX());
              mInvalidate |= INVALIDATE_DO;
              return true;
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_POINTER_UP:
              mActionMode = ACTION_MODE_NONE;
              return true;
        }
        return false;
    }

    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
     }

    private void savedActionData() {
        mActionZoomSavedPosX = mMainImage.getTranslateX();
        mActionZoomSavedPosY = mMainImage.getTranslateY();
        mActionZoomSavedScale = mScale;
    }

    private synchronized void move(float x , float y) {
        float scale = mScale;

        float moveX = x;
        float moveY = y;

        if(mWidth < mMainImage.getWidth() * scale) {
            if(moveX > 0) moveX = 0;
            else if(mWidth / scale > mMainImage.getWidth() + moveX) moveX = mWidth / scale - mMainImage.getWidth();
        } else {
            moveX = (mWidth / scale - mMainImage.getWidth()) / 2;
        }

        if(mHeight < mMainImage.getHeight() * scale) {
            if(moveY > 0) moveY = 0;
            else if(mHeight / scale > mMainImage.getHeight() + moveY) moveY = mHeight / scale - mMainImage.getHeight();
        } else {
            moveY = (mHeight / scale - mMainImage.getHeight()) / 2;
        }

        mMainImage.move(moveX, moveY);
        mInvalidate |= INVALIDATE_DO;
    }
}
